package org.bdware.irs.irp.handle;

import com.nimbusds.jose.jwk.JWK;
import org.bdware.irp.client.IrpClientImpl;
import org.bdware.irp.exception.IrpClientException;
import org.bdware.irs.IrsBootWithTcp;
import org.bdware.irs.backend.servicesImpl.BDIrsStorageImpl;
import org.bdware.irs.backend.servicesImpl.StateInfoStorageRocksDBImpl;
import com.google.gson.JsonParser;

import org.bdware.irp.irplib.util.GlobalUtils;
import org.apache.log4j.Logger;
import org.bdware.irp.irplib.core.IrpMessage;
import org.bdware.irp.irplib.core.IrpMessageCode;
import org.bdware.irp.irplib.core.IrpRequest;
import org.bdware.irp.irplib.core.IrpResponse;
import org.bdware.irp.irpserver.IrpServerInfo;
import org.bdware.irp.irpserver.IrsHandler;
import org.bdware.irp.stateinfo.StateInfoBase;

import java.io.IOException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import org.bdware.irs.backend.utils.DOIParser;

public class IrsHandlerImpl implements IrsHandler {
    Logger logger = Logger.getLogger(IrsHandlerImpl.class);
    IrpServerInfo serverInfo;
    BDIrsStorageImpl store;

    public IrsHandlerImpl(IrpServerInfo info){
        serverInfo = info;
        store = new BDIrsStorageImpl();
    }

    @Override
    public IrpMessage doidResolve(IrpMessage request){
        IrpRequest req = (IrpRequest) request;
        StateInfoBase stateInfoBase = null;
        String doi = req.getDoid();
        stateInfoBase = store.getStateInfoByID(doi);

        IrpMessage res;
        if(stateInfoBase != null) {
            logger.debug("Resolve doid success: " + req.getDoid());
            Map<String, String> values = new Gson().fromJson(stateInfoBase.getHandleValues(), Map.class);
            res = IrpResponse.newIrsResolveResponse(req.getDoid(), values);
        }else {
            logger.debug("Resolve doid failed: " + req.getDoid());
            res = IrpResponse.newErrorResponse(
                    IrpMessageCode.OC_RESOLUTION_DOID,
                    IrpMessageCode.RC_ERROR,
                    "resolve doid error!");
        }
        return res;
    }

    @Override
    public IrpMessage batchCreateDOID(IrpMessage request){
        IrpRequest req = (IrpRequest) request;
        StateInfoBase stateInfoBase = request2StateInfo(req.getDoid(), req.getDoidValuesByMap());
        String userId = GlobalUtils.decodeString(req.credential.signerDoid);
        StateInfoBase userInfo = store.getStateInfoByID(userId);
        //get the pk from the database
        String pkInfo = userInfo.handleValues.get("pubkey").getAsString();
        boolean result = GlobalUtils.verifySigByJWK(req.encodedMessageBody, req.getSignature(), pkInfo);

        IrpMessage res;
        if(result == true && req.createNumber > 0){
            List<String> doids = new ArrayList<String>();
            for(int i = 0; i < req.createNumber; i++){
                String doid = store.createStateInfo(stateInfoBase, "do");
                doids.add(doid);
            }
            if(doids != null) {
                logger.debug("Create doid list  success, number : " + doids.size());
                res = IrpResponse.newIrsBatchCreateDoidResponse(doids);
            }else {
                logger.debug("Create doid failed!");
                res = IrpResponse.newErrorResponse(IrpMessageCode.OC_BATCH_CREATE_DOID, IrpMessageCode.RC_ERROR,
                    "Create doid error!");
            }
        }else{
            logger.debug("Auth the user failed!");
            res = IrpResponse.newErrorResponse(IrpMessageCode.OC_BATCH_CREATE_DOID, IrpMessageCode.RC_ERROR,
                    "Auth the user failed!");
        }
        return res;
    }

    @Override
    public IrpMessage createDOID(IrpMessage request){
        IrpRequest req = (IrpRequest) request;
        StateInfoBase stateInfoBase = request2StateInfo(req.getDoid(), req.getDoidValuesByMap());
        String userId = GlobalUtils.decodeString(req.credential.signerDoid);
        StateInfoBase userInfo = store.getStateInfoByID(userId);
        //get the pk from the database
        String pkInfo = userInfo.handleValues.get("pubkey").getAsString();
        boolean result = GlobalUtils.verifySigByJWK(req.encodedMessageBody, req.getSignature(), pkInfo);

        IrpMessage res;
        if(result == true){
            String doid = store.createStateInfo(stateInfoBase, "do");
            if(doid != null) {
                logger.debug("Create doid success: " + doid);
                res = IrpResponse.newIrsCreateDoidResponse(doid);
            }else {
                logger.debug("Create doid failed!");
                res = IrpResponse.newErrorResponse(IrpMessageCode.OC_CREATE_DOID, IrpMessageCode.RC_ERROR,
                        "Create doid error!");
            }
        }else{
            logger.debug("Auth the user failed!");
            res = IrpResponse.newErrorResponse(IrpMessageCode.OC_CREATE_DOID, IrpMessageCode.RC_ERROR,
                    "Auth the user failed!");
        }
        return res;
    }

    @Override
    public IrpMessage updateDOID(IrpMessage request) {
        IrpRequest req = (IrpRequest) request;
        StateInfoBase stateInfoBase = request2StateInfo(req.getDoid(), req.getDoidValuesByMap());
        String userId = GlobalUtils.decodeString(req.credential.signerDoid);
        StateInfoBase userInfo = store.getStateInfoByID(userId);
        //get the pk from the database
        String pkInfo = userInfo.handleValues.get("pubkey").getAsString();
        boolean result = GlobalUtils.verifySigByJWK(req.encodedMessageBody, req.getSignature(), pkInfo);

        IrpMessage res;
        if(result == true){
            StateInfoBase stateInfoBaseNew = store.updateStateInfo(stateInfoBase);
            if(stateInfoBaseNew != null) {
                logger.debug("Update doid success: " + req.getDoid());
                res = IrpResponse.newIrsUpdateDoidResponse(stateInfoBaseNew.getIdentifier());
            }else {
                logger.debug("Update doid failed: " + req.getDoid());
                res = IrpResponse.newErrorResponse(IrpMessageCode.OC_UPDATE_DOID, IrpMessageCode.RC_ERROR,
                    "Update doid error!");
            }
        }else{
            logger.debug("Auth the user failed!");
            res = IrpResponse.newErrorResponse(IrpMessageCode.OC_UPDATE_DOID, IrpMessageCode.RC_ERROR,
                    "Auth the user failed!");
        }
        return res;
    }

    @Override
    public IrpMessage deleteDOID(IrpMessage request){
        IrpRequest req = (IrpRequest) request;
        String userId = GlobalUtils.decodeString(req.credential.signerDoid);
        //userId = "86.5000.470/dou.9e8dc889-8ccd-482e-afdc-8fc50a22dde9";
        StateInfoBase userInfo = store.getStateInfoByID(userId);
        //get the pk from the database
        String pkInfo = userInfo.handleValues.get("pubkey").getAsString();
        boolean result = GlobalUtils.verifySigByJWK(req.encodedMessageBody, req.getSignature(), pkInfo);

        IrpMessage res;
        if(result == true) {
            boolean resultMsg = store.deleteStateInfoByID(req.getDoid());
            if (resultMsg == true) {
                logger.debug("delete doid success: " + req.getDoid());
                res = IrpResponse.newIrsDeleteDoidResponse(req.getDoid());
            } else {
                logger.debug("delete doid failed: " + req.getDoid());
                res = IrpResponse.newErrorResponse(IrpMessageCode.OC_DELETE_DOID, IrpMessageCode.RC_ERROR,
                        "Delete doid error!");
            }
        }else{
            logger.debug("Auth the user failed!");
            res = IrpResponse.newErrorResponse(IrpMessageCode.OC_DELETE_DOID, IrpMessageCode.RC_ERROR,
                    "Auth the user failed!");
        }
        return res;
    }

    public static StateInfoBase request2StateInfo(String identifier, Map<String, String> doidValues){
        String values = new Gson().toJson(doidValues);
        JsonObject jsonObject = JsonParser.parseString(values).getAsJsonObject();

        StateInfoBase stateInfoBase = new StateInfoBase();
        stateInfoBase.setIdentifier(identifier);
        stateInfoBase.setHandleValues(jsonObject);
        return stateInfoBase;
    }
}
